package com.netx.ebs;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;

import com.netx.generics.basic.Checker;
import com.netx.generics.basic.Context;
import com.netx.generics.sql.Column;
import com.netx.generics.sql.Table;
import com.netx.generics.time.Moment;
import com.netx.generics.collections.ImmutableList;
import com.netx.generics.collections.ImmutableMap;
import com.netx.data.DatabaseException;
import com.netx.data.NoSuchInstanceException;
import com.netx.data.ValueList;
import com.netx.data.Field;


public class Errors extends EbsEntity {

	public Errors() {
	}
	
	public long save(long userId, Throwable t, String requestURL) throws DatabaseException {
		Checker.checkNull(t, "t");
		return _save(userId==0 ? null : userId, t.getMessage(), t, requestURL);
	}

	public long save(long userId, String message, String requestURL) throws DatabaseException {
		Checker.checkEmpty(message, "message");
		return _save(userId==0 ? null : userId, message, null, requestURL);
	}

	public ImmutableList<Error> find(long userID) throws DatabaseException {
		User user = Entities.getUsers(getConnection()).find(userID);
		if(user == null) {
			throw new NoSuchInstanceException("userID: "+userID);
		}
		else {
			Table data = getConnection().select("error_id, request_url, message, time_ocurred, stack_trace FROM errors WHERE user_id="+userID);
			List<Error> errors = new ArrayList<Error>();
			for(int i=0; i<data.getRowCount(); i++) {
				errors.add(new Error(data.getRow(i), user));
			}
			return new ImmutableList<Error>(errors);
		}
	}
	
	public ImmutableList<Error>[] getAllByUser() throws DatabaseException {
		return getAllByUser("username");
	}
	
	public ImmutableList<Error>[] getAllByUser(String orderField) throws DatabaseException {
		Checker.checkEmpty(orderField, "orderField");
		// Check whether the orderField exists:
		ImmutableMap<String,Field> fields = getMetaData().getFields();
		if(fields.get(orderField) == null) {
			throw new IllegalArgumentException("\""+orderField+"\" is not a valid field for entity \"users\"");
		}
		else {
			Table table = getConnection().select("user_id, "+orderField+" FROM users ORDERBY "+orderField);
			Column userIds = table.getColumn(1);
			List<ImmutableList<Error>> list = new ArrayList<ImmutableList<Error>>();
			for(int i=0; i<userIds.getRowCount(); i++) {
				ImmutableList<Error> errors = find(userIds.getLong(i));
				if(!errors.isEmpty()) {
					list.add(errors);
				}
			}
			@SuppressWarnings("unchecked")
			ImmutableList<Error>[] result = (ImmutableList<Error>[])list.toArray(new ImmutableList[0]);
			return result;
		}
	}
	
	public ImmutableList<Error> getAllByDate() throws DatabaseException {
		Table data = getConnection().select("error_id, request_url, message, time_ocurred, stack_trace FROM errors ORDERBY time_thrown");
		Users users = Entities.getUsers(getConnection());
		List<Error> errors = new ArrayList<Error>();
		Map<Long,User> userCache = new HashMap<Long,User>();
		for(int i=0; i<data.getRowCount(); i++) {
			long userCode = data.getLong(8, i);
			// reuse already fetched users:
			User u = (User)userCache.get(new Long(userCode));
			if(u == null) {
				u = users.find(userCode);
				userCache.put(new Long(userCode), u);
			}
			errors.add(new Error(data.getRow(i), u));
		}
		return new ImmutableList<Error>(errors);
	}
	
	public void remove(long errorID) throws DatabaseException {
		if(!exists(errorID)) {
			throw new NoSuchInstanceException("errorID: "+errorID);
		}
		else {
			delete(errorID);
		}
	}

	public boolean removeFor(long userID) throws DatabaseException {
		User user = Entities.getUsers(getConnection()).find(userID);
		if(user == null) {
			throw new NoSuchInstanceException("userID: "+userID);
		}
		else {
			return delete("user_id="+userID) != 0;
		}
	}

	private long _save(Long userId, String message, Throwable t, String requestURL) throws DatabaseException {
		if(Context.getRootContext().getProperty("ebs/save-errors-in-database").booleanValue()) {
			Context.getRootLogger().error(message, t);
			return -1;
		}
		else {
			ValueList values = new ValueList();
			values.addValue("user_id", userId);
			values.addValue("message", message);
			StringWriter stackTrace = new StringWriter();
			t.printStackTrace(new PrintWriter(stackTrace));
			values.addValue("stack_trace", stackTrace.toString());
			values.addValue("request_url", requestURL);
			values.addValue("time_occurred", new Moment());
			return insert(values);
		}
	}
}
